package com.github.chirspan.xaas.soa.message.processor;

import com.github.chirspan.xaas.soa.message.core.MessageModel;
import com.github.chirspan.xaas.soa.message.core.SOAMessage;
import com.github.chirspan.xaas.soa.message.processor.AbstractMessageProcessor;
import com.github.chirspan.xaas.soa.message.schema.MessageSchemaDefinition;
import com.github.chirspan.xaas.soa.message.util.XmlHelper;
import com.github.chirspan.xaas.soa.message.xml.XMLMessageUtils;
import com.github.chirspan.xaas.soa.message.xml.namespace.MapNamespaceContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.StringReader;
import java.util.Map;

;

@Slf4j
public class XMLMessageProcessor extends AbstractMessageProcessor {

    private final XMLMessageUtils xmlMessageUtils = new XMLMessageUtils();

    @Override
    public String getMessageType() {
        return "XML";
    }

    protected Document stringToDocument(String message)
            throws SAXException, IOException, ParserConfigurationException {
        Document doc;
        if ((message == null) || ("".equalsIgnoreCase(message.trim()))) {
            return null;
        }
        try {
            doc = messageToDocument(message);
        } catch (SAXException e) {
            log.warn("Xml解析失败！！！");
            String t = XmlHelper.removeInvalidCharRefs(message);
            doc = messageToDocument(t);
        }
        return doc;
    }

    protected Document messageToDocument(String message)
            throws ParserConfigurationException, SAXException, IOException {
        StringReader sr = new StringReader(message);
        InputSource is = new InputSource(sr);
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        factory.setNamespaceAware(true);
        DocumentBuilder builder = factory.newDocumentBuilder();
        Document doc = builder.parse(is);

        return doc;
    }

    protected String documentToString(Document doc)
            throws TransformerException {
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer transformer = tf.newTransformer();
        transformer.setOutputProperty("encoding", "utf-8");
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        transformer.transform(new DOMSource(doc), new StreamResult(bos));

        return bos.toString();
    }

    @Override
    public SOAMessage generate(MessageModel message,
                               MessageSchemaDefinition msd) {
        if (message != null && msd != null) {
            try {
                Map<String, Object> content = message.getContent();
                Map<String, Object> schema = msd.getSchema();
                String xmlTemplate = msd.getTemplate();

                if (content == null || schema == null
                        || StringUtils.isEmpty(xmlTemplate)) {
                    return null;
                }

                Document doc = stringToDocument(xmlTemplate);
                MapNamespaceContext nsc = new MapNamespaceContext(msd.getNsMap());

                xmlMessageUtils.fillNode(doc, schema, nsc, content);

                return new SOAMessage(documentToString(doc), msd);
            } catch (Exception e) {
                getStackTrace(e);
                log.error(e.toString());
            }
        }

        return null;
    }

    @Override
    public MessageModel parse(SOAMessage message) throws Exception {
        if (message != null) {
            String strMessage = (String) message.getMessage();
            MessageSchemaDefinition msd = message.getMessageSchemaDef();

            if (StringUtils.isEmpty(strMessage) || msd == null) {
                return null;
            }
            Map<String, Object> schema = msd.getSchema();

            if (schema == null) {
                return null;
            }

            try {
                Document doc = stringToDocument(strMessage);
                MapNamespaceContext nsc = new MapNamespaceContext(msd.getNsMap());

                MessageModel gm = new MessageModel();
                gm.setContent(xmlMessageUtils.getNodeContent(doc, schema, nsc));
                return gm;
            } catch (ParserConfigurationException e) {
                log.error(e.toString());
                throw new ParserConfigurationException(e.toString());
            } catch (SAXException e) {
                log.error(e.toString());
                throw new SAXException(e.toString());
            } catch (IOException e) {
                log.error(e.toString());
                throw new IOException(e.toString());
            }
        }

        return null;
    }
}
